package com.yanqu.road.server.manager.activity.apprentice;

import com.yanqu.road.dao.impl.activity.apprentice.ApprenticeUserDataDaoImpl;
import com.yanqu.road.entity.activity.apprentice.*;
import com.yanqu.road.entity.activity.apprentice.config.*;
import com.yanqu.road.entity.activity.apprentice.data.ApprenticeBuffProjectData;
import com.yanqu.road.entity.activity.apprentice.data.ApprenticeUserData;
import com.yanqu.road.entity.activity.apprentice.data.ApprenticeUserExamData;
import com.yanqu.road.entity.activity.apprentice.enums.*;
import com.yanqu.road.entity.enums.eGamePlayerEventType;
import com.yanqu.road.entity.enums.eOccupationType;
import com.yanqu.road.entity.log.cross.apprentice.LogCrossApprenticeDoProject;
import com.yanqu.road.entity.log.cross.apprentice.LogCrossApprenticeExam;
import com.yanqu.road.entity.log.cross.apprentice.LogCrossApprenticeLevel;
import com.yanqu.road.entity.log.cross.apprentice.LogCrossApprenticeProjectEvent;
import com.yanqu.road.entity.player.UserBaseInfo;
import com.yanqu.road.entity.rank.cross.CrossUserRank;
import com.yanqu.road.exception.BusinessException;
import com.yanqu.road.logic.cross.CrossActivityRankListModel;
import com.yanqu.road.pb.activity.ApprenticeProto;
import com.yanqu.road.server.logic.MessageHelper;
import com.yanqu.road.server.manager.activity.ActivityRankMgr;
import com.yanqu.road.server.manager.activity.apprentice.handler.param.ApprenticeCountSortAddRateReq;
import com.yanqu.road.server.manager.activity.apprentice.handler.param.ApprenticeCountTalentAddRateReq;
import com.yanqu.road.server.manager.activity.apprentice.handler.param.ApprenticeDoProjectReq;
import com.yanqu.road.server.manager.activity.apprentice.handler.param.ApprenticeDoProjectResp;
import com.yanqu.road.server.manager.activity.apprentice.pb.ApprenticePb;
import com.yanqu.road.server.manager.log.CrossAutoLogMgr;
import com.yanqu.road.server.manager.user.CrossUserMgr;
import com.yanqu.road.server.protocol.GameErrorCode;
import com.yanqu.road.server.protocol.Protocol;
import com.yanqu.road.utils.RandomHelper;
import com.yanqu.road.utils.YanQuMessageUtils;
import com.yanqu.road.utils.property.Property;
import com.yanqu.road.utils.property.PropertyHelper;
import com.yanqu.road.utils.string.StringUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;

public class CrossApprenticeActivity {

    /**
     * logger
     */
    private static Logger logger = LogManager.getLogger(CrossApprenticeActivity.class.getName());

    /**
     * 随机器
     */
    private Random random = new Random();

    /**
     * 活动ID
     */
    private int activityId;

    /**
     * 活动配置
     */
    private ApprenticeConfig config;

    /**
     * 玩家数据MAP
     */
    private Map<Long, ApprenticeUserData> userDataMap = new ConcurrentHashMap<>();

    /**
     * 商会玩家数据，K：商会ID, K：玩家ID
     */
    private Map<String, Map<Long, ApprenticeUserData>> unionUserMap = new ConcurrentHashMap<>();

    /**
     * 构造函数
     * @param config
     */
    public CrossApprenticeActivity(ApprenticeConfig config) {
        this.activityId = config.getActivityInfo().getActivityId();
        this.config = config;
        //加载数据
        this.reloadData();
    }

    /**
     * 加载数据库数据
     */
    private void reloadData() {
        ApprenticeUserDataDaoImpl dao = new ApprenticeUserDataDaoImpl();
        this.userDataMap = dao.getApprenticeUserDataMap(this.activityId);
        for (ApprenticeUserData userData : this.userDataMap.values()) {
            if (!StringUtils.isNullOrEmpty(userData.getUnionId())) {
                Map<Long, ApprenticeUserData> itemMap = this.unionUserMap.computeIfAbsent(userData.getUnionId(), k -> new ConcurrentHashMap<>());
                itemMap.put(userData.getUserId(), userData);
            }
        }
    }

    /**
     * 存储数据
     */
    public void save() {
        //玩家数据
        for (ApprenticeUserData data : this.userDataMap.values()) {
            if (data.isInsertOption()) {
                new ApprenticeUserDataDaoImpl().add(data);
            } else if (data.isUpdateOption()) {
                new ApprenticeUserDataDaoImpl().update(data);
            }
        }
    }

    /**
     * 获取活动ID
     * @return
     */
    public int getActivityId() {
        return activityId;
    }

    /**
     * 刷新配置
     * @param config
     */
    public void refreshConfig(ApprenticeConfig config) {
        this.config = config;
    }

    /**
     * 获取配置
     * @return
     */
    public ApprenticeConfig getConfig() {
        return config;
    }

    /**
     * 初始化玩家数据
     * @param userId
     */
    private synchronized ApprenticeInitUserResult initUserData(long userId) throws BusinessException {
        ApprenticeInitUserResult result = new ApprenticeInitUserResult();
        ApprenticeUserData userData = this.userDataMap.get(userId);
        if (userData == null) {
            UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
            ApprenticeConfig config = this.getConfig();
            //可被宴请的次数
            List<Integer> banquetParam = config.getAPPRENTICE_BANQUET_FAMILY_PARAM();
            int canBeBanquetNum = banquetParam.get(0);
            userData = new ApprenticeUserData(
                    activityId,
                    userId,
                    userBaseInfo.getServerId(),
                    userBaseInfo.getUnionUid(),
                    0,
                    null,
                    canBeBanquetNum
            );
            //创建小考数据
            ApprenticeUserExamData examData = this.createExamData(1, userData);
            userData.setExamData(examData);
            //获取待选择项目
            List<Integer> waitProjectIds = config.getFirstProjectIdList();
            userData.setWaitProjects(waitProjectIds);
            this.userDataMap.put(userId, userData);
            //商会玩家MAP
            if (!StringUtils.isNullOrEmpty(userBaseInfo.getUnionUid())) {
                Map<Long, ApprenticeUserData> itemMap = this.unionUserMap.computeIfAbsent(userBaseInfo.getUnionUid(), k -> new ConcurrentHashMap<>());
                itemMap.put(userId, userData);
            }
            //初始化玩家奖励
            result.setReward(config.getAPPRENTICE_PLAYER_GUIDE_GET_ITEM());
        }
        result.setUserData(userData);
        return result;
    }

    /**
     * 获取玩家数据
     * @param userId
     * @return
     */
    public ApprenticeUserData getUserData(long userId) {
        return this.userDataMap.get(userId);
    }

    /**
     * 同步玩家数据
     * @param userId
     */
    public void syncUserData(long userId) {
        ApprenticeUserData userData = this.userDataMap.get(userId);
        if (userData != null) {
            ApprenticeProto.ApprenticeUserData.Builder builder = ApprenticePb.buildUserData(userData, this.getCanSelectProjectNum(userData));
            ApprenticeProto.ApprenticeSyncUserDataMsg.Builder msg = ApprenticeProto.ApprenticeSyncUserDataMsg.newBuilder();
            msg.setUserData(builder);
            MessageHelper.sendPacket(userData.getServerId(), userData.getUserId(), YanQuMessageUtils.buildMessage(Protocol.U_APPRENTICE_SYNC_USER_DATA, msg));
        }
    }

    /**
     * 创建小考数据
     * @param examIndex 第几次小考
     * @param userData 玩家数据
     * @return
     * @throws BusinessException
     */
    public ApprenticeUserExamData createExamData(int examIndex, ApprenticeUserData userData) throws BusinessException {
        //获取等级配置
        int level = this.config.getLevelByExp(userData.getAllTalent());
        ApprenticeLevelConfig levelConfig = this.config.getLevelConfig(level);
        if (levelConfig == null) {
            throw new BusinessException(GameErrorCode.E_APPRENTICE_LEVEL_CONFIG_NOT_FOUND);
        }
        //小考行程数
        userData.setExamDiffNum(levelConfig.getTestInterval());
        //目标天资
        List<ApprenticeTalentData> targetTalentList = this.createExamTargetTalentList(levelConfig.getTestParam());
        //创建小考数据
        ApprenticeUserExamData examData = new ApprenticeUserExamData();
        for (ApprenticeTalentData talentData : targetTalentList) {
            examData.setTargetTalent(talentData.getType(), talentData.getValue());
        }
        examData.setLevelConfigId(levelConfig.getId());
        examData.setExamIndex(examIndex);
        examData.setTalentReward(levelConfig.getTestRewardList());
        examData.setCreateTime(System.currentTimeMillis());
        return examData;
    }

    /**
     * 创建小考目标天资
     * @param testValue test配置的test字段
     * @return
     * @throws BusinessException
     */
    private List<ApprenticeTalentData> createExamTargetTalentList(long testValue) throws BusinessException {
        //随机出一个模版
        ApprenticeModelConfig modelConfig = this.config.randomModelConfig();
        if (modelConfig == null) {
            throw new BusinessException(GameErrorCode.E_APPRENTICE_TEST_MODEL_NOT_FOUND);
        }
        List<Integer> testParamList = new ArrayList<>(modelConfig.getTestParamList());
        //计算目标天资
        RandomHelper random = new RandomHelper();
        long oneTestValue = testValue / 5;
        List<ApprenticeTalentData> list = new ArrayList<>();
        for (eOccupationType type : eOccupationType.values()) {
            //随机出一个计算rate
            int index = this.random.nextInt(testParamList.size());
            int rate = testParamList.remove(index);
            //计算目标天资
            BigDecimal rateBig = BigDecimal.valueOf(rate).divide(BigDecimal.valueOf(1000), 8, RoundingMode.UP);
            logger.info("爱徒养成-创建小考目标天资-模板值：{}", rate);
            int rate2 = random.next(this.config.getAPPRENTICE_TEST_FLUCTUATE_RANGE().get(0), this.config.getAPPRENTICE_TEST_FLUCTUATE_RANGE().get(1)) + 1;
            BigDecimal rateBig2 = BigDecimal.valueOf(rate2).divide(BigDecimal.valueOf(1000), 8, RoundingMode.UP);
            logger.info("爱徒养成-创建小考目标天资-浮动值：{}", rate2);
            long targetTalent = BigDecimal.valueOf(oneTestValue).multiply(rateBig).multiply(rateBig2).setScale(0, RoundingMode.UP).longValue();
            ApprenticeTalentData talentData = new ApprenticeTalentData(type.getValue(), targetTalent);
            list.add(talentData);
        }
        return list;
    }

    /**
     * 获取我的排行
     * @return
     */
    public int getMyRank(ApprenticeUserData userData) {
        CrossActivityRankListModel rankListModel = ActivityRankMgr.getCrossActivityNewRankListModel(this.activityId, eGamePlayerEventType.ApprenticeScoreRank.getValue());
        int rank = -1;
        if (rankListModel != null) {
            rank = rankListModel.getMyRank(userData.getUserId(), userData.getServerId());
        }
        return rank;
    }

    /**
     * 获取玩家ID集合,根据排名返回
     * @param startRank
     * @param endRank
     * @param myUserId
     * @return
     */
    public Set<Long> getUserSetByRanks(int startRank, int endRank, long myUserId) {
        Set<Long> result = new HashSet<>();
        CrossActivityRankListModel rankListModel = ActivityRankMgr.getCrossActivityNewRankListModel(this.activityId, eGamePlayerEventType.ApprenticeScoreRank.getValue());
        if (rankListModel != null) {
            LinkedList<CrossUserRank> rankLinkedList = rankListModel.getRankList();
            for (int rank = startRank; rank <= endRank; rank++) {
                int index = rank - 1;
                if (index < 0 || index >= rankLinkedList.size()) {
                    continue;
                }
                CrossUserRank crossUserRank = rankLinkedList.get(index);
                if (crossUserRank.getUserId() != myUserId) {
                    result.add(crossUserRank.getUserId());
                }
            }
        }
        return result;
    }

    /**
     * 获取商会成员MAP
     * @param unionId
     * @return
     */
    public Map<Long, ApprenticeUserData> getUnionUserMap(String unionId) {
        return this.unionUserMap.get(unionId);
    }

    /**
     * 可被宴请的商会玩家MAP
     * @param userData
     * @return
     */
    public Map<Long, ApprenticeUserData> getCanBeBanquetUserMap(ApprenticeUserData userData) {
        String unionId = userData.getUnionId();
        long userId = userData.getUserId();
        Map<Long, ApprenticeUserData> newUnionUserMap = new HashMap<>();
        if (StringUtils.isNullOrEmpty(unionId)) {
            return newUnionUserMap;
        }
        Map<Long, ApprenticeUserData> unionUserMap = this.getUnionUserMap(unionId);
        if (unionUserMap == null) {
            return newUnionUserMap;
        }
        for (ApprenticeUserData member : unionUserMap.values()) {
            if (member.getCanBeBanquetNum() > 0) {
                if (userId != member.getUserId()) {
                    newUnionUserMap.put(member.getUserId(), member);
                }
            }
        }
        return newUnionUserMap;
    }

    /**
     * 获取PVP玩家
     * @param userData
     * @return
     */
    public ApprenticeUserData getPvpUserData(ApprenticeUserData userData) {
        //获取我的排行
        int myRank = this.getMyRank(userData);
        if (myRank <= 0) {
            return null;
        }
        //获取排行范围
        List<Integer> rankRange = this.getConfig().getAPPRENTICE_PVP_RANGE();
        if (rankRange.isEmpty()) {
            return null;
        }
        int start = rankRange.get(0);
        int end = rankRange.get(1);
        //排名-排名*(配置值/1000-1）
        BigDecimal myRankBig = BigDecimal.valueOf(myRank);
        BigDecimal startRate = BigDecimal.valueOf(end).divide(BigDecimal.valueOf(1000), 8, RoundingMode.DOWN).subtract(BigDecimal.ONE);
        int startRank = myRankBig.subtract(myRankBig.multiply(startRate)).setScale(0, RoundingMode.UP).intValue();
        BigDecimal endRate = BigDecimal.valueOf(start).divide(BigDecimal.valueOf(1000), 8, RoundingMode.DOWN).subtract(BigDecimal.ONE);
        int endRank = myRankBig.subtract(myRankBig.multiply(endRate)).setScale(0, RoundingMode.UP).intValue();
        //获取玩家ID集合,根据排名返回
        Set<Long> targetUserIdSet = this.getUserSetByRanks(startRank, endRank, userData.getUserId());
        if (targetUserIdSet.isEmpty()) {
            return null;
        }
        //随机出一个对方玩家ID
        Long targetUserId = RandomHelper.getRandomElement(targetUserIdSet);
        if (targetUserId == null) {
            return null;
        }
        //获取该排名的玩家
        return this.getUserData(targetUserId);
    }

    /**
     * 进入活动
     * @param userId
     * @return
     */
    public String enterActivity(long userId) throws BusinessException {
        ApprenticeUserData userData = this.getUserData(userId);
        String initReward = "";
        if (userData == null) {
            ApprenticeInitUserResult initUserResult = this.initUserData(userId);
            userData = initUserResult.getUserData();
            initReward = initUserResult.getReward();
        }
        //判断商会是否有变更
        UserBaseInfo userBaseInfo = CrossUserMgr.getUserBaseInfo(userId);
        if (!Objects.equals(userData.getUnionId(), userBaseInfo.getUnionUid())) {
            //清除原商会的数据
            if (!Objects.equals(userData.getUnionId(), "")) {
                Map<Long, ApprenticeUserData> itemMap = this.unionUserMap.get(userData.getUnionId());
                if (itemMap != null) {
                    itemMap.remove(userId);
                }
            }
            //注入新商会的数据
            userData.setUnionId(userBaseInfo.getUnionUid());
            if (!Objects.equals(userBaseInfo.getUnionUid(), "")) {
                Map<Long, ApprenticeUserData> itemMap = this.unionUserMap.computeIfAbsent(userBaseInfo.getUnionUid(), k -> new ConcurrentHashMap<>());
                itemMap.put(userId, userData);
            }
        }
        return initReward;
    }

    /**
     * 执行项目
     * @param userId
     * @param projectIds
     * @throws BusinessException
     */
    public ApprenticeDoProjectResult doProjects(long userId, List<Integer> projectIds, int level) throws BusinessException {
        //获取玩家数据
        ApprenticeUserData userData = this.getUserData(userId);
        if (userData == null) {
            throw BusinessException.newException(GameErrorCode.E_APPRENTICE_USER_DATA_NOT_FOUND);
        }
        //锁玩家
        try {
            if (!userData.getLock().tryLock(5, TimeUnit.SECONDS)) {
                logger.error("爱徒养成-执行项目-加锁-超时：{}, {}", userId, projectIds);
                throw BusinessException.newException(GameErrorCode.E_APPRENTICE_REQUEST_TIME_OUT);
            }
            //获取配置
            ApprenticeConfig config = this.getConfig();
            //等级配置
            ApprenticeLevelConfig levelConfig = config.getLevelConfig(userData.getLevel());
            if (levelConfig == null) {
                throw BusinessException.newException(GameErrorCode.E_APPRENTICE_LEVEL_CONFIG_NOT_FOUND);
            }
            //判断等级是否正确
            if (level != userData.getLevel()) {
                throw BusinessException.newException(GameErrorCode.E_APPRENTICE_USER_LEVEL_ERROR);
            }
            //判断选择的项目数量是否正确
            this.checkProjectNum(userData, projectIds);
            //判断项目是否正确
            List<Integer> waitProjects = new ArrayList<>(userData.getWaitProjects());
            for (int projectId : projectIds) {
                if (!waitProjects.contains(projectId)) {
                    throw BusinessException.newException(GameErrorCode.E_APPRENTICE_PROJECT_ID_ERROR);
                } else {
                    waitProjects.remove(Integer.valueOf(projectId));
                }
            }

            //行程结果
            ApprenticeProgramResult programResult = new ApprenticeProgramResult();

            //buff触发：自由行程洗牌
            projectIds = this.buffTriggerChangeProjectIds(userData, projectIds);

            //立即生效buff注入玩家数据
            this.addBuffByProjectIds(userData, projectIds, ApprenticeProjectTriggerTimeEnum.CURRENT);

            //本次生效特殊效果结果集
            List<ApprenticeEffectBuffProject> effectBuffProjects = new ArrayList<>();

            //BUFF触发：仅能选择一个日程，随后将自动将其安排满整个行程
            projectIds = this.buffTriggerOnlySelectOne(userData, projectIds, effectBuffProjects);

            //BUFF触发：是否随机天资类型
            boolean randomTalentTypeFlag = this.buffTriggerIsRandomTalentType(userData);

            //BUFF触发：获取职业加成千分比
            Map<Integer, Integer> talentAddRateMap = this.buffTriggerGetAddTalentTypeRate(userData, projectIds, effectBuffProjects);

            //BUFF触发：获取品质加成千分比
            Map<Integer, Integer> sortAddRateMap = this.buffTriggerGetAddSortTypeRate(userData, effectBuffProjects);

            //执行项目
            ApprenticeDoProjectResult result = new ApprenticeDoProjectResult();
            List<ApprenticeDoOneProjectResult> oneProjectResultList = new ArrayList<>();
            for (int projectId : projectIds) {
                //项目配置
                ApprenticeProjectConfig projectConfig = config.getProjectConfig(projectId);
                if (projectConfig == null) {
                    logger.error("爱徒养成-执行项目-项目配置不存在：{}，{}，{}", userId, activityId, projectId);
                    continue;
                }
                //添加天资
                String[] ScoreArr = projectConfig.getScore().split("\\|");
                int talentType = Integer.parseInt(ScoreArr[0]);//天资类型
                if (talentType == 0 || randomTalentTypeFlag) {
                    //随机天资类型
                    talentType = this.random.nextInt(5) + 1;
                }
                long talentValue = Long.parseLong(ScoreArr[1]);//天资值
                //计算加成
                talentValue = this.countTalentValue(talentType, projectConfig.getSort(), talentValue, talentAddRateMap, sortAddRateMap, levelConfig.getConsume());
                ApprenticeTalentData addValueResult = userData.addTalent(talentType, talentValue, true);

                //处理项目
                ApprenticeDoProjectReq req = new ApprenticeDoProjectReq();
                req.setType(projectConfig.getType());
                req.setActivity(this);
                req.setUserData(userData);
                req.setProjectConfig(projectConfig);
                req.setEffectBuffProjects(effectBuffProjects);
                req.setTalentAddRateMap(talentAddRateMap);
                req.setSortAddRateMap(sortAddRateMap);
                ApprenticeDoProjectResp doProjectResp = CrossApprenticeMgr.doProject(req);

                //注入结果
                if (doProjectResp != null) {
                    //宴请世家增加的天资汇进总增加天资数据
                    if (doProjectResp.getBanquetResult() != null && doProjectResp.getBanquetResult().getMyAddTalent() != null) {
                        programResult.addTalentData(doProjectResp.getBanquetResult().getMyAddTalent());
                    }
                    //天资辩学增加的天资汇进总增加天资数据
                    if (doProjectResp.getPvpResult() != null && doProjectResp.getPvpResult().getAddTalent() != null) {
                        programResult.addTalentData(doProjectResp.getPvpResult().getAddTalent());
                    }
                }
                programResult.addTalentData(addValueResult);
                ApprenticeDoOneProjectResult oneProjectResult = new ApprenticeDoOneProjectResult();
                oneProjectResult.setProjectId(projectId);
                oneProjectResult.setTalentData(addValueResult);
                if (doProjectResp != null) {
                    oneProjectResult.setBanquetResult(doProjectResp.getBanquetResult());
                    oneProjectResult.setPvpResult(doProjectResp.getPvpResult());
                }
                oneProjectResultList.add(oneProjectResult);
            }
            result.setOneProjectResults(oneProjectResultList);

            //行程奖励
            int costNum = levelConfig.getConsume();
            Property reward = new Property();
            for (int i = 0; i < costNum; i++) {
                Property oneReward = PropertyHelper.parseNewStringToProperty(config.getAPPRENTICE_ITEM_USE_REWARD());
                reward.addProperty(oneReward);
            }
            programResult.setReward(PropertyHelper.parsePropertyToString(reward));

            //小考处理
            int examResultIndex = 0;//触发小考的小考index
            try {
                ApprenticeUserExamData examResult = this.examHandle(userData, programResult);
                if (examResult != null) {
                    examResultIndex = examResult.getExamIndex();
                    result.setExamData(examResult);
                }
            } catch (BusinessException e) {
                logger.error("爱徒养成-小考处理-创建小考数据失败：", e);
            }

            //是否升级
            int newLevel = this.config.getLevelByExp(userData.getAllTalent());
            if (newLevel > userData.getLevel()) {
                userData.setLevel(newLevel);
                result.setUpgradeFlag(true);
                //日志
                LogCrossApprenticeLevel log = new LogCrossApprenticeLevel(
                        activityId,
                        userId,
                        level,
                        newLevel,
                        userData.talentToSting()
                );
                CrossAutoLogMgr.add(log);
            }

            //本次生效BUFF
            effectBuffProjects.addAll(userData.getNextEffectProject());
            userData.clearNextEffectProject();//移除
            programResult.setSpecialProjectIds(effectBuffProjects);

            //下次生效buff注入玩家数据
            this.addBuffByProjectIds(userData, projectIds, ApprenticeProjectTriggerTimeEnum.NEXT);

            //随出下一次待选择项目
            userData.setProgramNum(userData.getProgramNum() + 1);
            List<Integer> waitProjectIds = this.getWaitProjectIds(userData, levelConfig.getChooseNum(), null, true);
            userData.setWaitProjects(waitProjectIds);

            //清除buff次数为0的buff
            this.delZeroNumBuff(userData);

            //日志
            LogCrossApprenticeDoProject log = new LogCrossApprenticeDoProject(
                    activityId,
                    userId,
                    userData.getProgramNum(),
                    StringUtils.listToString(projectIds, ","),
                    levelConfig.getConsume(),
                    programResult.getReward(),
                    programResult.talentRerawdToString(),
                    examResultIndex,
                    programResult.specialProjectToString()
            );
            CrossAutoLogMgr.add(log);

            //返回
            result.setUserData(userData);
            result.setProgramResult(programResult);
            return result;
        } catch (InterruptedException e) {
            logger.error("爱徒养成-执行项目-tryLock报错：{}", e);
            throw BusinessException.newException(GameErrorCode.E_APPRENTICE_REQUEST_TIME_OUT);
        } finally {
            userData.getLock().unlock();
        }
    }

    /**
     * 添加BUFF
     * @param userData 玩家素具
     * @param projectIds 执行的项目ID
     * @param triggerTime 触发时机
     */
    private void addBuffByProjectIds(ApprenticeUserData userData, List<Integer> projectIds, ApprenticeProjectTriggerTimeEnum triggerTime) {
        for (int projectId : projectIds) {
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(projectId);
            if (projectConfig == null) {
                continue;
            }
            //是否是特殊项目
            if (projectConfig.getSort() != ApprenticeProjectSortEnum.SORT_TYPE_0.getType()) {
                continue;
            }
            //触发时机一致
            if (triggerTime.getType() != projectConfig.getTriggerTime()) {
                continue;
            }
            //添加BUFF
            userData.addBuffProjectId(projectConfig.getId(), projectConfig.getContinueRound());
        }
    }

    /**
     * 小考处理
     * @param userData
     * @return
     * @throws BusinessException
     */
    private ApprenticeUserExamData examHandle(ApprenticeUserData userData, ApprenticeProgramResult programResult) throws BusinessException {
        ApprenticeUserExamData examData = null;
        //小考距离天数-1
        userData.reExamDiffNum();
        //判断是否达到小考
        if (userData.getExamDiffNum() <= 0) {
            //小考结果
            examData = userData.getExamData();
            //发放小考奖励
            int successNum = examData.getSuccessNum();
            //小考天资奖励
            List<Integer> rewardList = examData.getTalentReward();
            List<String> addTalentList = new ArrayList<>();
            if (successNum > 0) {
                int index = successNum - 1;
                if (index >= rewardList.size()) {
                    index = rewardList.size() - 1;
                }
                long addTalentValue = rewardList.get(index);
                for (eOccupationType type : eOccupationType.values()) {
                    ApprenticeTalentData addTalentData = userData.addTalent(type.getValue(), addTalentValue, false);
                    programResult.addTalentData(addTalentData);
                    addTalentList.add(addTalentData.talentToString());
                }
            }
            //日志
            LogCrossApprenticeExam log = new LogCrossApprenticeExam(
                    userData.getActivityId(),
                    userData.getUserId(),
                    examData.getExamIndex(),
                    userData.getLevel(),
                    successNum,
                    StringUtils.listToString(addTalentList, ",")
            );
            CrossAutoLogMgr.add(log);
            //创建下一次小考
            ApprenticeUserExamData newExamData = this.createExamData(examData.getExamIndex() + 1, userData);//创建小考数据
            userData.setExamData(newExamData);
        }
        return examData;
    }

    /**
     * 清除buff次数为0的buff
     * @param userData
     */
    private void delZeroNumBuff(ApprenticeUserData userData) {
        Iterator<ApprenticeBuffProjectData> iterator = userData.getBuffProjectIds().iterator();
        while (iterator.hasNext()) {
            ApprenticeBuffProjectData item = iterator.next();
            if (item.getNum() <= 0) {
                iterator.remove(); // 安全地删除元素
            }
        }
    }

    /**
     * 计算加成后的天资
     * @param talentType 职业类型
     * @param sortType 品质类型
     * @param talentValue 天资值
     * @param talentAddRateMap 职业加成rate map
     * @param sortAddRateMap 品质加成rate map
     * @param costNum 消耗体力数
     * @return
     */
    public long countTalentValue(int talentType, int sortType, long talentValue, Map<Integer, Integer> talentAddRateMap, Map<Integer, Integer> sortAddRateMap, int costNum) {
        int talentAddRate = talentAddRateMap.getOrDefault(talentType, 0);
        int sortAddRate = sortAddRateMap.getOrDefault(sortType, 0);
        BigDecimal addRate = BigDecimal.valueOf(talentAddRate + sortAddRate + 1000).divide(BigDecimal.valueOf(1000), 8, RoundingMode.HALF_UP);
        BigDecimal costNumBig = BigDecimal.valueOf(costNum);
        return BigDecimal.valueOf(talentValue).multiply(addRate).multiply(costNumBig).setScale(0, RoundingMode.UP).longValue();
    }

    /**
     * 校验项目数量
     * @param userData
     * @param selectProjectIds
     * @throws BusinessException
     */
    private void checkProjectNum(ApprenticeUserData userData, List<Integer> selectProjectIds) throws BusinessException {
        int projectNum = selectProjectIds.size();
        //可选择项目数量
        int canSelectProjectNum = this.getCanSelectProjectNum(userData);
        if (projectNum != canSelectProjectNum) {
            throw BusinessException.newException(GameErrorCode.E_APPRENTICE_SELECT_PROJECT_NUM_ERROR);
        }
        //BUFF触发：专注训练-只能选一个项目
        boolean onlySelectOneProjectFlag = this.buffTriggerOnlySelectOneProject(userData);
        if (onlySelectOneProjectFlag) {
            //选择的项目不能是特殊项目
            int projectId = selectProjectIds.get(0);
            ApprenticeProjectConfig projectConfig = this.getConfig().getProjectConfig(projectId);
            if (projectConfig != null) {
                if (projectConfig.getSort() == ApprenticeProjectSortEnum.SORT_TYPE_0.getType()) {
                    throw BusinessException.newException(GameErrorCode.E_APPRENTICE_ONLY_SELECT_NORMAL_PROJECT);
                }
            }
        }
    }

    /**
     * 获取可选择的项目数量
     * @param userData
     * @return
     */
    public int getCanSelectProjectNum(ApprenticeUserData userData) {
        ApprenticeLevelConfig levelConfig = config.getLevelConfig(userData.getLevel());
        int projectNum = levelConfig.getProjectNum();
        //BUFF触发：专注训练-只能选一个项目
        boolean onlySelectOneProjectFlag = this.buffTriggerOnlySelectOneProject(userData);
        if (onlySelectOneProjectFlag) {
            return 1;
        }
        return projectNum + this.buffTriggerGetAddProjectNum(userData);
    }

    /**
     * 获取待选项目
     * @param userData
     * @param projectNum 项目数量
     * @param exProjectIds 排除的项目ID
     * @param reBuffFlag 是否减少buff次数
     * @return
     */
    private List<Integer> getWaitProjectIds(ApprenticeUserData userData, int projectNum, Set<Integer> exProjectIds, boolean reBuffFlag) {
        //需要排除的项目
        Set<Integer> excludeProjectIds = this.getExcludeProjectIds(userData);
        if (exProjectIds != null && !exProjectIds.isEmpty()) {
            excludeProjectIds.addAll(exProjectIds);
        }
        //BUFF触发：变更等级
        int addLevel = this.buffTriggerGetAddLevel(userData);
        int randomProjectLevel = userData.getLevel() + addLevel;
        //BUFF触发：指定项目职业
        Set<Integer> pointTalentTypeSet = this.buffTriggerPointTalentType(userData, reBuffFlag);
        //BUFF触发：指定项目品质
        Set<Integer> ponitSortTypeSet = this.buffTriggerPointSortType(userData, reBuffFlag);
        //是否触发保底
        boolean isGuarantee = this.config.isTriggerGuarantee(userData.getProgramNum(), userData.getAllTalent());
        //随出
        return this.config.randomProjectIdsByLevel(randomProjectLevel, projectNum, excludeProjectIds, ponitSortTypeSet, pointTalentTypeSet, isGuarantee);
    }

    /**
     * 获取排除项目
     * @param userData
     * @return
     */
    private Set<Integer> getExcludeProjectIds(ApprenticeUserData userData) {
        //排除掉配置的排除项目
        Set<Integer> excludeProjectIds = new HashSet<>();
        for (ApprenticeBuffProjectData buffProjectData :userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(buffProjectData.getProjectId());
            excludeProjectIds.addAll(projectConfig.getConflictIdSet());
        }
        //宴请世家是否条件到达：1.玩家有商会，2.有可邀请同商会玩家，3.次数合法
        Map<Long, ApprenticeUserData> canBeBanquetUserMap = this.getCanBeBanquetUserMap(userData);
        if (StringUtils.isNullOrEmpty(userData.getUnionId()) || canBeBanquetUserMap.isEmpty() || userData.getStartBanquetNum() >= this.config.getAPPRENTICE_BANQUET_FAMILY_PARAM().get(3)) {
            excludeProjectIds.add(ApprenticeProjectIdEnum.PROJECT_ID_10044.getId());
        }
        //天资辩学是否条件到达：榜单上能找到对手
        ApprenticeUserData pvpUserData = this.getPvpUserData(userData);
        if (pvpUserData == null) {
            excludeProjectIds.add(ApprenticeProjectIdEnum.PROJECT_ID_10027.getId());
        }
        return excludeProjectIds;
    }

    /***************************************************BUFF*****************************************************/

    /**
     * BUFF触发：指定项目职业
     * @param userData
     * @return
     */
    private Set<Integer> buffTriggerPointTalentType(ApprenticeUserData userData, boolean reBuffFlag) {
        for (ApprenticeBuffProjectData buffProjectData : userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_2.getType()) {
                //buff次数减一
                if (reBuffFlag) {
                    buffProjectData.addNum(-1);
                    //记录下生效buff，用于显示
                    ApprenticeEffectBuffProject effectBuffProject = new ApprenticeEffectBuffProject(buffProjectData.getProjectId());
                    userData.addNextEffectProject(effectBuffProject);
                }
                //职业
                int talentType = Integer.parseInt(projectConfig.getParam());
                Set<Integer> talentTypeSet = new HashSet<>();
                talentTypeSet.add(talentType);
                return talentTypeSet;
            }
        }
        return null;
    }

    /**
     * buff触发：指定项目品质
     * @param userData
     * @return
     */
    private Set<Integer> buffTriggerPointSortType(ApprenticeUserData userData, boolean reBuffFlag) {
        for (ApprenticeBuffProjectData buffProjectData :userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_11.getType()) {
                //buff次数减一
                if (reBuffFlag) {
                    buffProjectData.addNum(-1);
                    //记录下生效buff，用于显示
                    ApprenticeEffectBuffProject effectBuffProject = new ApprenticeEffectBuffProject(buffProjectData.getProjectId());
                    userData.addNextEffectProject(effectBuffProject);
                }
                //指定品质
                Set<Integer> sortTypeSet = new HashSet<>();
                String[] paramArr = projectConfig.getParam().split("\\|");
                for (String param : paramArr) {
                    int sortType = Integer.parseInt(param);
                    if (sortType != ApprenticeProjectSortEnum.SORT_TYPE_0.getType()) {
                        sortTypeSet.add(sortType);
                    }
                }
                return sortTypeSet;
            }
        }
        return null;
    }

    /**
     * buff触发：获取变更的等级
     * @param userData
     * @return
     */
    public int buffTriggerGetAddLevel(ApprenticeUserData userData) {
        for (ApprenticeBuffProjectData buffProjectData : userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_4.getType()) {
                //指定品质
                String[] paramArr = projectConfig.getParam().split("\\|");
                return Integer.parseInt(paramArr[0]);
            }
        }
        return 0;
    }

    /**
     * BUFF触发：获取天资加成（职业维度）
     * @param userData
     * @return
     */
    public Map<Integer, Integer> buffTriggerGetAddTalentTypeRate(ApprenticeUserData userData, List<Integer> selectProjectIds, List<ApprenticeEffectBuffProject> effectBuffProjects) {
        Map<Integer, Integer> resultMap = new HashMap<>();
        for (ApprenticeBuffProjectData buffProjectData :userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(buffProjectData.getProjectId());
            ApprenticeCountTalentAddRateReq req = new ApprenticeCountTalentAddRateReq();
            req.setType(projectConfig.getType());
            req.setActivity(this);
            req.setBuffProjectData(buffProjectData);
            req.setUserData(userData);
            req.setProjectConfig(projectConfig);
            req.setResultMap(resultMap);
            req.setSelectProjectIds(selectProjectIds);
            req.setEffectBuffProjects(effectBuffProjects);
            CrossApprenticeMgr.countTalentTypeAddRate(req);
        }
        return resultMap;
    }

    /**
     * BUFF触发：获取天资加成（品质维度）
     * @param userData
     * @return
     */
    public Map<Integer, Integer> buffTriggerGetAddSortTypeRate(ApprenticeUserData userData, List<ApprenticeEffectBuffProject> effectBuffProjects) {
        Map<Integer, Integer> resultMap = new HashMap<>();
        for (ApprenticeBuffProjectData buffProjectData :userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = config.getProjectConfig(buffProjectData.getProjectId());
            ApprenticeCountSortAddRateReq req = new ApprenticeCountSortAddRateReq();
            req.setType(projectConfig.getType());
            req.setBuffProjectData(buffProjectData);
            req.setUserData(userData);
            req.setProjectConfig(projectConfig);
            req.setResultMap(resultMap);
            req.setEffectBuffProjects(effectBuffProjects);
            CrossApprenticeMgr.countSortTypeAddRate(req);
        }
        return resultMap;
    }

    /**
     * BUFF触发：获取增加的项目数量
     * @param userData
     * @return
     */
    public int buffTriggerGetAddProjectNum(ApprenticeUserData userData) {
        //判断是否有增减项目buff
        for (ApprenticeBuffProjectData buffProjectData : userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = this.config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig == null) {
                continue;
            }
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_15.getType()) {
                //增加项目数量
                String[] paramArr = projectConfig.getParam().split("\\|");
                return Integer.parseInt(paramArr[0]);
            }
        }
        return 0;
    }

    /**
     * BUFF触发：只能选一个项目
     * @param userData
     * @return
     */
    public boolean buffTriggerOnlySelectOneProject(ApprenticeUserData userData) {
        //判断是否有增减项目buff
        for (ApprenticeBuffProjectData buffProjectData : userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = this.config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig == null) {
                continue;
            }
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_9.getType()) {
                return true;
            }
        }
        return false;
    }

    /**
     * BUFF触发：是否随机天资类型
     * @param userData
     * @return
     */
    public boolean buffTriggerIsRandomTalentType(ApprenticeUserData userData) {
        //判断是否有变更天资职业buff
        for (ApprenticeBuffProjectData buffProjectData : userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = this.config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig == null) {
                continue;
            }
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_7.getType()) {
                //天资职业全随机
                return true;
            }
        }
        return false;
    }

    /**
     * BUFF触发：仅能选择一个日程，随后将自动将其安排满整个行程
     * @param userData
     * @param selectProjectIds
     * @param effectBuffProjects 触发的buff
     * @return
     */
    public List<Integer> buffTriggerOnlySelectOne(ApprenticeUserData userData, List<Integer> selectProjectIds, List<ApprenticeEffectBuffProject> effectBuffProjects) {
        //等级配置
        ApprenticeLevelConfig levelConfig = config.getLevelConfig(userData.getLevel());
        //判断自身buff是否有类型9
        for (ApprenticeBuffProjectData buffProjectData : userData.getBuffProjectIds()) {
            if (buffProjectData.getNum() <= 0) {
                continue;
            }
            //项目配置
            ApprenticeProjectConfig projectConfig = this.config.getProjectConfig(buffProjectData.getProjectId());
            if (projectConfig == null) {
                continue;
            }
            if (projectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_9.getType()) {
                //buff次数减一
                buffProjectData.addNum(-1);
                effectBuffProjects.add(new ApprenticeEffectBuffProject(buffProjectData.getProjectId()));
                //获取项目数量
                int projectNum = levelConfig.getProjectNum() + this.buffTriggerGetAddProjectNum(userData);
                //仅能选择一个日程，随后将自动将其安排满整个行程
                int selectProjectId = selectProjectIds.get(0);
                List<Integer> newSelectProjectIds = new ArrayList<>();
                for (int i = 1; i <= projectNum; i++) {
                    newSelectProjectIds.add(selectProjectId);
                }
                return newSelectProjectIds;
            }
        }
        return selectProjectIds;
    }

    /**
     * buff触发：洗牌
     * @param userData
     * @param selectProjectIds
     * @return
     */
    public List<Integer> buffTriggerChangeProjectIds(ApprenticeUserData userData, List<Integer> selectProjectIds) {
        //等级配置
        ApprenticeLevelConfig levelConfig = config.getLevelConfig(userData.getLevel());
        //判断选择的项目是否有类型14
        for (int selectProjectId : selectProjectIds) {
            ApprenticeProjectConfig selectProjectConfig = this.config.getProjectConfig(selectProjectId);
            if (selectProjectConfig.getType() == ApprenticeProjectTypeEnum.TYPE_14.getType()) {
                //有类型14，重新随机
                ApprenticeProjectConfig projectConfig = this.config.getProjectConfig(selectProjectId);
                if (projectConfig != null) {
                    //【自由行程】随机卡牌，不减少buff次数。重新随机除【自由行程】外的其他项目
                    List<Integer> waitProjectIds = new ArrayList<>();
                    waitProjectIds.add(projectConfig.getId());
                    //重新随机其他项目，重随项目时排除掉【自由行程】
                    Set<Integer> exProjectIds = new HashSet<>();
                    exProjectIds.add(projectConfig.getId());
                    //获取项目数量
                    int projectNum = levelConfig.getProjectNum() - 1 + this.buffTriggerGetAddProjectNum(userData);
                    List<Integer> newWaitProjectIds = this.getWaitProjectIds(userData, projectNum, exProjectIds, false);
                    waitProjectIds.addAll(newWaitProjectIds);
                    return waitProjectIds;
                }
            }
        }
        return selectProjectIds;
    }

    /**
     * debug
     * @param userId
     * @param code
     * @param param
     * @throws BusinessException
     */
    public void debug(long userId, String code, String param) throws BusinessException {
        ApprenticeUserData userData = this.userDataMap.get(userId);
        if (userData == null) {
            throw new BusinessException(GameErrorCode.E_APPRENTICE_USER_DATA_NOT_FOUND);
        }
        //指定项目
        if (code.equals("/appr-pp")) {
            List<Integer> waitProjects = StringUtils.stringToIntegerList(param, ",");
            userData.setWaitProjects(waitProjects);
        }
        //指定项目
        if (code.equals("/appr-initUser")) {
            this.userDataMap.remove(userId);
            this.enterActivity(userId);
        }
    }

    /**
     * 预留修复数据方法
     */
    public void repairHot() {

    }
}
